InputActionVisualizer.cs (12435B)
1 using System; 2 using System.Collections.Generic; 3 4 ////TODO: support ProcessEventsManually 5 ////TODO: add way to pick by player index 6 7 // Some fields assigned through only through serialization. 8 #pragma warning disable CS0649 9 10 namespace UnityEngine.InputSystem.Samples 11 { 12 /// <summary> 13 /// A component for debugging purposes that adds an on-screen display which shows 14 /// activity on an input action over time (<see cref="InputActionVisualizer.Visualization.Interaction"/>) 15 /// or an action's current value (<see cref="InputActionVisualizer.Visualization.Value"/>). 16 /// </summary> 17 /// <seealso cref="InputControlVisualizer"/> 18 [AddComponentMenu("Input/Debug/Input Action Visualizer")] 19 [ExecuteInEditMode] 20 public class InputActionVisualizer : InputVisualizer 21 { 22 /// <summary> 23 /// The action that is being visualized. May be null. 24 /// </summary> 25 public InputAction action => m_Action; 26 27 protected void FixedUpdate() 28 { 29 if (m_Visualization != Visualization.Value || m_Action == null || m_Visualizer == null) 30 return; 31 if (InputSystem.settings.updateMode != InputSettings.UpdateMode.ProcessEventsInFixedUpdate) 32 return; 33 RecordValue(Time.fixedTime); 34 } 35 36 protected void Update() 37 { 38 if (m_Visualization != Visualization.Value || m_Action == null || m_Visualizer == null) 39 return; 40 if (InputSystem.settings.updateMode != InputSettings.UpdateMode.ProcessEventsInDynamicUpdate) 41 return; 42 RecordValue(Time.time); 43 } 44 45 protected new void OnEnable() 46 { 47 if (m_Visualization == Visualization.None) 48 return; 49 50 base.OnEnable(); 51 52 ResolveAction(); 53 SetupVisualizer(); 54 55 if (s_EnabledInstances == null) 56 s_EnabledInstances = new List<InputActionVisualizer>(); 57 if (s_EnabledInstances.Count == 0) 58 InputSystem.onActionChange += OnActionChange; 59 s_EnabledInstances.Add(this); 60 } 61 62 protected new void OnDisable() 63 { 64 base.OnDisable(); 65 66 s_EnabledInstances.Remove(this); 67 if (s_EnabledInstances.Count == 0) 68 InputSystem.onActionChange -= OnActionChange; 69 70 if (m_Visualization == Visualization.Interaction && m_Action != null) 71 { 72 m_Action.started -= OnActionTriggered; 73 m_Action.performed -= OnActionTriggered; 74 m_Action.canceled -= OnActionTriggered; 75 } 76 } 77 78 protected new void OnGUI() 79 { 80 if (m_Visualization == Visualization.None) 81 return; 82 83 if (Event.current.type != EventType.Repaint) 84 return; 85 86 base.OnGUI(); 87 88 if (m_ShowControlName && m_ActiveControlName != null) 89 VisualizationHelpers.DrawText(m_ActiveControlName, new Vector2(m_Rect.x, m_Rect.yMax), 90 VisualizationHelpers.ValueTextStyle); 91 } 92 93 private void RecordValue(double time) 94 { 95 Debug.Assert(m_Action != null); 96 Debug.Assert(m_Visualizer != null); 97 98 var value = m_Action.ReadValueAsObject(); 99 m_Visualizer.AddSample(value, time); 100 101 if (m_ShowControlName) 102 RecordControlName(); 103 } 104 105 private void RecordControlName() 106 { 107 var control = m_Action.activeControl; 108 if (control == m_ActiveControl) 109 return; 110 111 m_ActiveControl = control; 112 m_ActiveControlName = control != null ? new GUIContent(control.path) : null; 113 } 114 115 private void ResolveAction() 116 { 117 // If we have a reference to an action, try that first. 118 if (m_ActionReference != null) 119 m_Action = m_ActionReference.action; 120 121 // If we didn't get an action from that but we have an action name, 122 // just search through the currently enabled actions for one that 123 // matches by name. 124 if (m_Action == null && !string.IsNullOrEmpty(m_ActionName)) 125 { 126 var slashIndex = m_ActionName.IndexOf('/'); 127 var mapName = slashIndex != -1 ? m_ActionName.Substring(0, slashIndex) : null; 128 var actionName = slashIndex != -1 ? m_ActionName.Substring(slashIndex + 1) : m_ActionName; 129 130 var enabledActions = InputSystem.ListEnabledActions(); 131 foreach (var action in enabledActions) 132 { 133 if (string.Compare(actionName, action.name, StringComparison.InvariantCultureIgnoreCase) != 0) 134 continue; 135 136 if (mapName != null && action.actionMap != null && string.Compare(mapName, action.actionMap.name, 137 StringComparison.InvariantCultureIgnoreCase) != 0) 138 continue; 139 140 m_Action = action; 141 break; 142 } 143 } 144 145 // If we still don't have an action, there's nothing much for us to do. 146 // The action may show up at a later point. 147 if (m_Action == null) 148 return; 149 150 if (m_Visualization == Visualization.Interaction) 151 { 152 m_Action.performed += OnActionTriggered; 153 m_Action.started += OnActionTriggered; 154 m_Action.canceled += OnActionTriggered; 155 } 156 } 157 158 private void SetupVisualizer() 159 { 160 m_Visualizer = null; 161 if (m_Action == null) 162 return; 163 164 switch (m_Visualization) 165 { 166 case Visualization.Value: 167 switch (m_Action.type) 168 { 169 case InputActionType.Button: 170 m_Visualizer = new VisualizationHelpers.ScalarVisualizer<float> 171 { 172 limitMax = 1 173 }; 174 break; 175 176 case InputActionType.Value: 177 case InputActionType.PassThrough: 178 if (!string.IsNullOrEmpty(m_Action.expectedControlType)) 179 { 180 var layout = InputSystem.LoadLayout(m_Action.expectedControlType); 181 if (layout != null) 182 { 183 var valueType = layout.GetValueType(); 184 if (valueType == typeof(float)) 185 m_Visualizer = new VisualizationHelpers.ScalarVisualizer<float> 186 { 187 limitMax = 1 188 }; 189 else if (valueType == typeof(int)) 190 m_Visualizer = new VisualizationHelpers.ScalarVisualizer<int> 191 { 192 limitMax = 1 193 }; 194 else if (valueType == typeof(Vector2)) 195 m_Visualizer = new VisualizationHelpers.Vector2Visualizer(); 196 } 197 } 198 break; 199 } 200 break; 201 202 case Visualization.Interaction: 203 // We don't really know which interactions are sitting on the action and its bindings 204 // and while we could do and perform work to find out, it's simpler to just wait until 205 // we get input and then whatever interactions we encounter as we go along. Also keeps 206 // the visualization a little less cluttered. 207 m_Visualizer = new VisualizationHelpers.TimelineVisualizer(); 208 break; 209 } 210 } 211 212 private void OnActionDisabled() 213 { 214 } 215 216 private void OnActionTriggered(InputAction.CallbackContext context) 217 { 218 Debug.Assert(m_Visualization == Visualization.Interaction); 219 220 var timelineName = "Default"; 221 var interaction = context.interaction; 222 if (interaction != null) 223 { 224 timelineName = interaction.GetType().Name; 225 if (timelineName.EndsWith("Interaction")) 226 timelineName = timelineName.Substring(0, timelineName.Length - "Interaction".Length); 227 } 228 229 var visualizer = (VisualizationHelpers.TimelineVisualizer)m_Visualizer; 230 var timelineIndex = visualizer.GetTimeline(timelineName); 231 if (timelineIndex == -1) 232 { 233 Color color; 234 timelineIndex = visualizer.timelineCount; 235 if (timelineIndex < s_InteractionColors.Length) 236 color = s_InteractionColors[timelineIndex]; 237 else 238 color = new Color(Random.value, Random.value, Random.value, 1); 239 240 visualizer.AddTimeline(timelineName, color); 241 if (timelineIndex > 0) 242 visualizer.showLegend = true; 243 } 244 245 var time = (float)context.time; 246 switch (context.phase) 247 { 248 case InputActionPhase.Canceled: 249 visualizer.AddSample(timelineIndex, 0f, time); 250 break; 251 252 case InputActionPhase.Performed: 253 visualizer.AddSample(timelineIndex, 1f, time); 254 visualizer.AddSample(timelineIndex, 0f, time); 255 break; 256 257 case InputActionPhase.Started: 258 visualizer.AddSample(timelineIndex, 0.5f, time); 259 break; 260 } 261 262 if (m_ShowControlName) 263 RecordControlName(); 264 } 265 266 private static void OnActionChange(object actionOrMap, InputActionChange change) 267 { 268 switch (change) 269 { 270 case InputActionChange.ActionEnabled: 271 case InputActionChange.ActionMapEnabled: 272 for (var i = 0; i < s_EnabledInstances.Count; ++i) 273 if (s_EnabledInstances[i].m_Action == null) 274 { 275 s_EnabledInstances[i].ResolveAction(); 276 if (s_EnabledInstances[i].m_Action != null) 277 s_EnabledInstances[i].SetupVisualizer(); 278 } 279 break; 280 281 case InputActionChange.ActionDisabled: 282 for (var i = 0; i < s_EnabledInstances.Count; ++i) 283 if (actionOrMap == s_EnabledInstances[i].m_Action) 284 s_EnabledInstances[i].OnActionDisabled(); 285 break; 286 287 case InputActionChange.ActionMapDisabled: 288 for (var i = 0; i < s_EnabledInstances.Count; ++i) 289 if (s_EnabledInstances[i].m_Action?.actionMap == actionOrMap) 290 s_EnabledInstances[i].OnActionDisabled(); 291 break; 292 } 293 } 294 295 [SerializeField] private Visualization m_Visualization; 296 [SerializeField] private InputActionReference m_ActionReference; 297 [SerializeField] private string m_ActionName; 298 [SerializeField] private bool m_ShowControlName; 299 300 [NonSerialized] private InputAction m_Action; 301 [NonSerialized] private InputControl m_ActiveControl; 302 [NonSerialized] private GUIContent m_ActiveControlName; 303 304 private static List<InputActionVisualizer> s_EnabledInstances; 305 private static readonly Color[] s_InteractionColors = 306 { 307 new Color(1, 0, 0, 1), 308 new Color(0, 0, 1, 1), 309 new Color(1, 1, 0, 1), 310 new Color(1, 0, 1, 1), 311 new Color(0, 1, 1, 1), 312 new Color(0, 1, 0, 1), 313 }; 314 315 public enum Visualization 316 { 317 None, 318 Value, 319 Interaction, 320 } 321 } 322 }